/*
 * $Id: string_compar.c 50 2009-02-10 05:48:05Z hyunghwan.chung $
 *
   Copyright 2005-2009 Chung, Hyung-Hwan.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an  BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

#if defined(__MSTRING)
#include <xp/bas/mstring.h>
#elif defined(__WSTRING)
#include <xp/bas/mstring.h>
#else
#include <xp/bas/string.h>
#endif

#include <xp/bas/ctype.h>

int xp_strcmp (const xp_char_t* s1, const xp_char_t* s2)
{
#if defined(_MSC_VER) && defined(_WIN32) && !defined(_WIN64) && defined(_M_IX86)
	_asm
	{
		mov ecx, s1                   ; ecx = s1
		mov edx, s2                   ; edx = s2

	next:
	#if defined(XP_CHAR_IS_MCHAR) && (XP_SIZEOF_CHAR==1)
		movsx eax, byte ptr [ecx]     ; eax = *s1
		movsx ebx, byte ptr [edx]     ; ebx = *s2
	#elif defined(XP_CHAR_IS_WCHAR) && (XP_SIZEOF_WCHAR_T==2)
		movsx eax, word ptr [ecx]     ; eax = *s1
		movsx ebx, word ptr [edx]     ; ebx = *s2
	#elif defined(XP_CHAR_IS_WCHAR) && (XP_SIZEOF_WCHAR_T==4)
		mov eax, dword ptr [ecx]      ; eax = *s1
		mov ebx, dword ptr [edx]      ; ebx = *s2
	#else
		#error unsupported char/wchar_t size
	#endif
		cmp eax, ebx                  ; if (eax != ebx) goto ret1
		jne ret1

		test eax, eax                 ; if (eax == 0) goto done
		jz done

	#ifdef XP_CHAR_IS_MCHAR
		add ecx, XP_SIZEOF_CHAR       ; s1++
		add edx, XP_SIZEOF_CHAR       ; s2++
	#else
		add ecx, XP_SIZEOF_WCHAR_T    ; s1++
		add edx, XP_SIZEOF_WCHAR_T    ; s2++
	#endif
		jmp next                      ; goto next

	ret1:
		xor ecx, ecx                  ; ecx = (eax > ebx)? 1: 0
		cmp eax, ebx              
		setg cl
		lea eax, dword ptr[ecx+ecx-1] ; eax = (1+1-1=>1,0+0-1=>-1)

	done:
	}

#else
	while (*s1 == *s2) 
	{
		if (*s1 == XP_CHAR('\0')) return 0;
		s1++, s2++;
	}

	return (*s1 > *s2)? 1: -1;
#endif
}

int xp_strcmpn (const xp_char_t* s1, const xp_char_t* s2, xp_size_t n)
{
#if defined(_MSC_VER) && defined(_WIN32) && !defined(_WIN64) && defined(_M_IX86)
	_asm
	{
		mov edi, n                    ; edi = n
		test edi, edi                 ; if (edi == 0) goto ret0
		jz ret0

		mov ecx, s1                   ; ecx = s1
		mov edx, s2                   ; edx = s2

	next:
	#if defined(XP_CHAR_IS_MCHAR) && (XP_SIZEOF_CHAR==1)
		movsx eax, byte ptr [ecx]     ; eax = *s1
		movsx ebx, byte ptr [edx]     ; ebx = *s2
	#elif defined(XP_CHAR_IS_WCHAR) && (XP_SIZEOF_WCHAR_T==2)
		movsx eax, word ptr [ecx]     ; eax = *s1
		movsx ebx, word ptr [edx]     ; ebx = *s2
	#elif defined(XP_CHAR_IS_WCHAR) && (XP_SIZEOF_WCHAR_T==4)
		mov eax, dword ptr [ecx]      ; eax = *s1
		mov ebx, dword ptr [edx]      ; ebx = *s2
	#else
		#error unsupported char/wchar_t size
	#endif

		cmp eax, ebx                  ; if (eax != ebx) goto ret1
		jne ret1

		test eax, eax                 ; if (eax == 0) goto done
		jz done

		cmp edi, 1                    ; if (n == 1) goto ret0
		je ret0

	#ifdef XP_CHAR_IS_MCHAR
		add ecx, XP_SIZEOF_CHAR       ; s1++
		add edx, XP_SIZEOF_CHAR       ; s2++
	#else
		add ecx, XP_SIZEOF_WCHAR_T    ; s1++
		add edx, XP_SIZEOF_WCHAR_T    ; s2++
	#endif
		sub edi, 1                    ; n--
		jmp next
			
	ret0:
		xor eax, eax                  ; eax = 0
		jmp done

	ret1:
		xor ecx, ecx                  ; ecx = (eax > ebx)? 1: 0
		cmp eax, ebx              
		setg cl
		lea eax, dword ptr[ecx+ecx-1] ; eax = (1+1-1=>1,0+0-1=>-1)

	done:
	}
#else
	if (n == 0) return 0;

	while (*s1 == *s2)
	{
		if (*s1 == XP_CHAR('\0') || n == 1) return 0;
		s1++, s2++, n--;
	}

	return (*s1 > *s2)? 1: -1;
#endif
}

int xp_strxcmp (const xp_char_t* s1, xp_size_t len, const xp_char_t* s2)
{
	const xp_char_t* end = s1 + len;
	while (s1 < end && *s2 != XP_CHAR('\0') && *s1 == *s2) s1++, s2++;
	if (s1 == end && *s2 == XP_CHAR('\0')) return 0;
	if (*s1 == *s2) return (s1 < end)? 1: -1;
	return (*s1 > *s2)? 1: -1;
}

int xp_strxncmp (
	const xp_char_t* s1, xp_size_t len1, 
	const xp_char_t* s2, xp_size_t len2)
{
	/*
	const xp_char_t* end1 = s1 + len1;
	const xp_char_t* end2 = s2 + len2;

	while (s1 < end1 && s2 < end2 && *s1 == *s2) s1++, s2++;
	if (s1 == end1 && s2 == end2) return 0;
	if (*s1 == *s2) return (s1 < end1)? 1: -1;
	return (*s1 > *s2)? 1: -1;
	*/

	xp_char_t c1, c2;
	const xp_char_t* end1 = s1 + len1;
	const xp_char_t* end2 = s2 + len2;

	while (s1 < end1)
	{
		c1 = *s1;
		if (s2 < end2) 
		{
			c2 = *s2;
			if (c1 > c2) return 1;
			if (c1 < c2) return -1;
		}
		else return 1;
		s1++; s2++;
	}

	return (s2 < end2)? -1: 0;
}

int xp_strcasecmp (const xp_char_t* s1, const xp_char_t* s2)
{
	xp_char_t c1, c2;

	c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	while (c1 == c2 && c2 != XP_CHAR('\0')) 
	{
		s1++, s2++;
		c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	}

	if (c1 > c2) return 1;
	else if (c1 < c2) return -1;
	return 0;
}

int xp_strcasecmpn (const xp_char_t* s1, const xp_char_t* s2, xp_size_t n)
{
	xp_char_t c1, c2;
	if (n == 0) return 0;

	do 
	{
		c1 = xp_toupper(*s1);
		c2 = xp_toupper(*s2);

		if (c1 == XP_CHAR('\0') || c1 != c2) break;
		s1++, s2++;
	} 
	while (--n > 0);

	if (c1 > c2) return 1;
	else if (c1 < c2) return -1;
	return 0;
}

int xp_strxcasecmp (const xp_char_t* s1, xp_size_t len, const xp_char_t* s2)
{
	xp_char_t c1, c2;
	const xp_char_t* end = s1 + len;

	c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	while (s1 < end && c2 != XP_CHAR('\0') && c1 == c2) 
	{
		s1++, s2++;
		c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	}
	if (s1 == end && c2 == XP_CHAR('\0')) return 0;
	if (c1 == c2) return (s1 < end)? 1: -1;
	return (c1 > c2)? 1: -1;
}

int xp_strxncasecmp (
	const xp_char_t* s1, xp_size_t len1, 
	const xp_char_t* s2, xp_size_t len2)
{
	/*
	xp_char_t c1, c2;
	const xp_char_t* end1 = s1 + len1;
	const xp_char_t* end2 = s2 + len2;

	c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	while (s1 < end1 && s2 < end2 && c1 == c2) 
	{
		s1++, s2++;
		c1 = xp_toupper(*s1); c2 = xp_toupper(*s2);
	}
	if (s1 == end1 && s2 == end2) return 0;
	if (c1 == c2) return (s1 < end1)? 1: -1;
	return (c1 > c2)? 1: -1;
	*/

	xp_char_t c1, c2;
	const xp_char_t* end1 = s1 + len1;
	const xp_char_t* end2 = s2 + len2;

	while (s1 < end1)
	{
		c1 = xp_toupper (*s1);
		if (s2 < end2) 
		{
			c2 = xp_toupper (*s2);
			if (c1 > c2) return 1;
			if (c1 < c2) return -1;
		}
		else return 1;
		s1++; s2++;
	}

	return (s2 < end2)? -1: 0;
}

